﻿using System;
using System.Collections.Generic;
using System.Text;

namespace BaiduMapTileCutter.GaoDe
{
    public class GoogleMapsProjection
    {
        private int TILE_SIZE = 256;
        private Point _pixelOrigin;
        private double _pixelsPerLonDegree;
        private double _pixelsPerLonRadian;

        public GoogleMapsProjection()
        {
            this._pixelOrigin = new Point(TILE_SIZE / 2.0, TILE_SIZE / 2.0);
            this._pixelsPerLonDegree = TILE_SIZE / 360.0;
            this._pixelsPerLonRadian = TILE_SIZE / (2 * Math.PI);
        }

        double bound(double val, double valMin, double valMax)
        {
            double res;
            res = Math.Max(val, valMin);
            res = Math.Min(val, valMax);
            return res;
        }

        double degreesToRadians(double deg)
        {
            return deg * (Math.PI / 180);
        }

        double radiansToDegrees(double rad)
        {
            return rad / (Math.PI / 180);
        }
     

        ///<summary>
        /// GPS经纬度换算成x,y坐标
        ///</summary>
        ///<param name="l">精度</param>
        ///<param name="B">纬度</param>
        ///<param name="xc">X坐标</param>
        ///<param name="yc">Y坐标</param>
        public void MCT84Bl2xy(double l, double B, out double xc, out double yc)
        {
            try
            {
                l = l * Math.PI / 180;
                B = B * Math.PI / 180;

                double B0 = 30 * Math.PI / 180;

                double N = 0, e = 0, a = 0, b = 0, e2 = 0, K = 0;
                a = 6378137;
                b = 6356752.3142;
                e = Math.Sqrt(1 - (b / a) * (b / a));
                e2 = Math.Sqrt((a / b) * (a / b) - 1);
                double CosB0 = Math.Cos(B0);
                N = (a * a / b) / Math.Sqrt(1 + e2 * e2 * CosB0 * CosB0);
                K = N * CosB0;

                double Pi = Math.PI;
                double SinB = Math.Sin(B);

                double tan = Math.Tan(Pi / 4 + B / 2);
                double E2 = Math.Pow((1 - e * SinB) / (1 + e * SinB), e / 2);
                double xx = tan * E2;

                xc = K * Math.Log(xx);
                yc = K * l;
                return;
            }
            catch (Exception ErrInfo)
            {
            }
            xc = -1;
            yc = -1;
        }

        public BMap.Point MCT84Bl2xy(double l, double B)
        {
            double xc = -1, yc = -1;
            try
            {
                l = l * Math.PI / 180;
                B = B * Math.PI / 180;

                double B0 = 30 * Math.PI / 180;

                double N = 0, e = 0, a = 0, b = 0, e2 = 0, K = 0;
                a = 6378137;
                b = 6356752.3142;
                e = Math.Sqrt(1 - (b / a) * (b / a));
                e2 = Math.Sqrt((a / b) * (a / b) - 1);
                double CosB0 = Math.Cos(B0);
                N = (a * a / b) / Math.Sqrt(1 + e2 * e2 * CosB0 * CosB0);
                K = N * CosB0;

                double Pi = Math.PI;
                double SinB = Math.Sin(B);

                double tan = Math.Tan(Pi / 4 + B / 2);
                double E2 = Math.Pow((1 - e * SinB) / (1 + e * SinB), e / 2);
                double xx = tan * E2;

                xc = K * Math.Log(xx);
                yc = K * l;
            }
            catch 
            {
            }
            return new BMap.Point(xc, yc);
        }


        public  double lngToPixel(double lng, int zoom)
        {
            return (lng + 180) * (256L << zoom) / 360;
        }
        public  double latToPixel(double lat, int zoom)
        {
            double siny = Math.Sin(lat * Math.PI / 180);
            double y = Math.Log((1 + siny) / (1 - siny));
            return (128 << zoom) * (1 - y / (2 * Math.PI));
        }
        /// <summary>
        /// 将经纬度转换成点
        /// </summary>
        /// <param name="lat"></param>
        /// <param name="lng"></param>
        /// <param name="zoom"></param>
        /// <returns></returns>
       public  BMap.Point fromLatLngToPoint(double lat, double lng, int zoom)
        {
            BMap.Point point = new BMap.Point(0, 0);

            point.X = _pixelOrigin.X + lng * _pixelsPerLonDegree;

            // Truncating to 0.9999 effectively limits latitude to 89.189. This is
            // about a third of a tile past the edge of the world tile.
            double siny = bound(Math.Sin(degreesToRadians(lat)), -0.9999, 0.9999);
            point.Y = _pixelOrigin.Y + 0.5 * Math.Log((1 + siny) / (1 - siny)) * -_pixelsPerLonRadian;

            int numTiles = 1 << zoom;
            point.X = point.X * numTiles;
            point.Y = point.Y * numTiles;
            return point;
        }

        public Point fromPointToLatLng(Point point, int zoom)
        {
            int numTiles = 1 << zoom;
            point.X = point.X / numTiles;
            point.Y = point.Y / numTiles;

            double lng = (point.X - _pixelOrigin.X) / _pixelsPerLonDegree;
            double latRadians = (point.Y - _pixelOrigin.Y) / -_pixelsPerLonRadian;
            double lat = radiansToDegrees(2 * Math.Atan(Math.Exp(latRadians)) - Math.PI / 2);
            return new Point(lat, lng);
        }

    }


}
